/*
 * Implementation file for the random number generator.
 */
#include "PhxRandom.h"

#include <cstdlib>
#include <ctime>

using namespace phx;

Random::Random()
{
    seed(0);
}

Random::Random(unsigned seed)
{
	Random::seed(seed);
}

void Random::seed(unsigned s)
{
    if (s == 0) 
	{
        s = (unsigned)clock();
    }

    // Fill the buffer with some basic random numbers.
    for (unsigned i = 0; i < 17; i++)
    {
        // Simple linear congruential generator.
        s = s * 2891336453 + 1;
        buffer[i] = s;
    }

    // Initialize pointers into the buffer.
    p1 = 0;  p2 = 10;
}

unsigned Random::RandomBits()
{
    unsigned result;

    // Rotate the buffer and store it back to itself.
    result = buffer[p1] = _lrotl(buffer[p2], 13) + _lrotl(buffer[p1], 9);

    // Rotate pointers
    if (--p1 < 0) p1 = 16;
    if (--p2 < 0) p2 = 16;

    // Return result
    return result;
}

#ifdef SINGLE_PRECISION
real Random::RandomReal()
{
    // Get the random number.
    unsigned bits = RandomBits();

    // Set up a reinterpret structure for manipulation.
    union 
	{
        real value;
        unsigned word;
    } convert;

    // Now assign the bits to the word. This works by fixing the ieee
    // sign and exponent bits (so that the size of the result is 1-2)
    // and using the bits to create the fraction part of the float.
    convert.word = (bits >> 9) | 0x3f800000;

    // Return the value.
    return convert.value - 1.0f;
}
#else
real Random::RandomReal()
{
    // Get the random number
    unsigned bits = RandomBits();

    // Set up a reinterpret structure for manipulation.
    union 
	{
        real value;
        unsigned words[2];
    } convert;

    // Now assign the bits to the words. This works by fixing the ieee
    // sign and exponent bits (so that the size of the result is 1-2)
    // and using the bits to create the fraction part of the float. Note
    // that bits are used more than once in this process.
    convert.words[0] =  bits << 20; // Fill in the top 16 bits.
    convert.words[1] = (bits >> 12) | 0x3FF00000; // And the bottom 20.

    // Return the value.
    return convert.value - 1.0;
}
#endif

real Random::RandomReal(real min, real max)
{
    return RandomReal() * (max-min) + min;
}

real Random::RandomReal(real scale)
{
    return RandomReal() * scale;
}

unsigned Random::RandomInt(unsigned max)
{
    return RandomBits() % max;
}

real Random::RandomBinomial(real scale)
{
    return (RandomReal() - RandomReal()) * scale;
}

Quaternion Random::RandomQuaternion()
{
    Quaternion q(
        RandomReal(),
        RandomReal(),
        RandomReal(),
        RandomReal()
        );
    q.Normalise();
    return q;
}

Vector3 Random::RandomVector(real scale)
{
    return Vector3(
        RandomBinomial(scale),
        RandomBinomial(scale),
        RandomBinomial(scale)
	);
}

Vector3 Random::RandomXZVector(real scale)
{
    return Vector3(
        RandomBinomial(scale),
        0,
        RandomBinomial(scale)
	);
}

Vector3 Random::RandomVector(const Vector3& scale)
{
    return Vector3(
        RandomBinomial(scale.x),
        RandomBinomial(scale.y),
        RandomBinomial(scale.z)
	);
}

Vector3 Random::RandomVector(const Vector3& min, const Vector3& max)
{
    return Vector3(
        RandomReal(min.x, max.x),
        RandomReal(min.y, max.y),
        RandomReal(min.z, max.z)
	);
}